home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Mail
/
pine3.92
/
pine
/
osdep
/
pipe
< prev
next >
Wrap
Text File
|
1996-03-05
|
11KB
|
381 lines
/*======================================================================
pipe
Initiate I/O to and from a process. These functions are similar to
popen and pclose, but both an incoming stream and an output file are
provided.
====*/
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
#define STDERR_FILENO 2
#endif
/*
* Defs to help fish child's exit status out of wait(2)
*/
#ifdef HAVE_WAIT_UNION
#define WaitType union wait
#ifndef WIFEXITED
#define WIFEXITED(X) (!(X).w_termsig) /* child exit by choice */
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(X) (X).w_retcode /* childs chosen exit value */
#endif
#else
#define WaitType int
#ifndef WIFEXITED
#define WIFEXITED(X) (!((X) & 0xff)) /* low bits tell how it died */
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(X) (((X) >> 8) & 0xff) /* high bits tell exit value */
#endif
#endif
/*
* Global's to helpsignal handler tell us child's status has changed...
*/
short child_signalled;
short child_jump = 0;
jmp_buf child_state;
/*----------------------------------------------------------------------
Spawn a child process and optionally connect read/write pipes to it
Args: command -- string to hand the shell
outfile -- address of pointer containing file to receive output
errfile -- address of pointer containing file to receive error output
mode -- mode for type of shell, signal protection etc...
Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
The outfile is either NULL, a pointer to a NULL value, or a pointer
to the requested name for the output file. In the pointer-to-NULL case
the caller doesn't care about the name, but wants to see the pipe's
results so we make one up. It's up to the programmer to make sure
the free storage containing the name is cleaned up.
Mode bits serve several purposes.
PIPE_WRITE tells us we need to open a pipe to write the child's
stdin.
PIPE_READ tells us we need to open a pipe to read from the child's
stdout/stderr. *NOTE* Having neither of the above set means
we're not setting up any pipes, just forking the child and exec'ing
the command. Also, this takes precedence over any named outfile.
PIPE_STDERR means we're to tie the childs stderr to the same place
stdout is going. *NOTE* This only makes sense then if PIPE_READ
or an outfile is provided. Also, this takes precedence over any
named errfile.
PIPE_PROT means to protect the child from the usual nasty signals
that might cause premature death. Otherwise, the default signals are
set so the child can deal with the nasty signals in its own way.
PIPE_NOSHELL means we're to exec the command without the aid of
a system shell. *NOTE* This negates the affect of PIPE_USER.
PIPE_USER means we're to try executing the command in the user's
shell. Right now we only look in the environment, but that may get
more sophisticated later.
PIPE_RESET means we reset the terminal mode to what it was before
we started pine and then exec the command.
----*/
PIPE_S *
open_system_pipe(command, outfile, errfile, mode)
char *command;
char **outfile, **errfile;
int mode;
{
PIPE_S *syspipe = NULL;
char shellpath[32], *shell;
int p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1;
dprint(5, (debugfile, "Opening pipe: \"%s\" (%s%s%s%s%s%s)\n",command,
(mode & PIPE_WRITE) ? "W":"", (mode & PIPE_READ) ? "R":"",
(mode & PIPE_NOSHELL) ? "N":"", (mode & PIPE_PROT) ? "P":"",
(mode & PIPE_USER) ? "U":"", (mode & PIPE_RESET) ? "T":""));
if(!(mode & PIPE_READ)){
if(outfile && !*outfile)
*outfile = temp_nam(NULL, "pine_p"); /* asked for, but not named? */
if(errfile && !*errfile)
*errfile = temp_nam(NULL, "pine_p"); /* ditto */
}
if(mode & (PIPE_WRITE | PIPE_READ)){
if(mode & PIPE_WRITE){
pipe(p); /* alloc pipe to write child */
oparentd = p[STDOUT_FILENO];
ichildd = p[STDIN_FILENO];
}
if(mode & PIPE_READ){
pipe(p); /* alloc pipe to read child */
iparentd = p[STDIN_FILENO];
ochildd = p[STDOUT_FILENO];
}
}
else{
flush_status_messages(0); /* just clean up display */
ClearScreen();
fflush(stdout);
}
syspipe = (PIPE_S *)fs_get(sizeof(PIPE_S));
memset(syspipe, 0, sizeof(PIPE_S));
if((syspipe->mode = mode) & PIPE_RESET)
Raw(0);
#ifdef SIGCHLD
/*
* Prepare for demise of child. Use SIGCHLD if it's available so
* we can do useful things, like keep the IMAP stream alive, while
* we're waiting on the child.
*/
child_signalled = child_jump = 0;
(void)signal(SIGCHLD, child_signal);
#endif
if((syspipe->pid = vfork()) == 0){
/* reset child's handlers in requested fashion... */
(void)signal(SIGINT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
(void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
(void)signal(SIGHUP, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
#ifdef SIGCHLD
(void) signal(SIGCHLD, SIG_DFL);
#endif
/* if parent isn't reading, and we have a filename to write */
if(!(mode & PIPE_READ) && outfile){ /* connect output to file */
int output = creat(*outfile, 0600);
dup2(output, STDOUT_FILENO);
if(mode & PIPE_STDERR)
dup2(output, STDERR_FILENO);
else if(errfile)
dup2(creat(*errfile, 0600), STDERR_FILENO);
}
if(mode & PIPE_WRITE){ /* connect process input */
close(oparentd);
dup2(ichildd, STDIN_FILENO); /* tie stdin to pipe */
close(ichildd);
}
if(mode & PIPE_READ){ /* connect process output */
close(iparentd);
dup2(ochildd, STDOUT_FILENO); /* tie std{out,err} to pipe */
if(mode & PIPE_STDERR)
dup2(ochildd, STDERR_FILENO);
else if(errfile)
dup2(creat(*errfile, 0600), STDERR_FILENO);
close(ochildd);
}
if(mode & PIPE_NOSHELL){
char **argv, **ap, *p, *cmd;
size_t n;
/* parse the arguments into a list */
for(cmd = cpystr(command); *cmd && isspace(*cmd); cmd++)
; /* swallow leading ws */
for(p = cmd, n = 2; *p; p++) /* count the args */
if(isspace(*p) && *(p+1) && !isspace(*(p+1)))
n++;
argv = ap = (char **)fs_get(n * sizeof(char *));
memset(argv, 0, n * sizeof(char *));
for(p = cmd; *p && !isspace(*p); p++)
;
if(*p) /* tie off command name */
*p++ = '\0';
*ap++ = cpystr(cmd);
while(*p){ /* collect args */
while(*p && isspace(*p))
*p++ = '\0';
*ap++ = (*p) ? p : NULL;
while(*p && !isspace(*p))
p++;
}
execvp(cmd, argv);
}
else{
if(mode & PIPE_USER){
char *env, *sh;
if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
shell = sh + 1;
strcpy(shellpath, env);
}
else{
shell = "csh";
strcpy(shellpath, "/bin/csh");
}
}
else{
shell = "sh";
strcpy(shellpath, "/bin/sh");
}
execl(shellpath, shell, command ? "-c" : 0, command, 0);
}
fprintf(stderr, "Can't exec %s\nReason: %s",
command, error_description(errno));
_exit(-1);
}
if(syspipe->pid > 0){
syspipe->isig = signal(SIGINT, SIG_IGN); /* Reset handlers to make */
syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to */
syspipe->hsig = signal(SIGHUP, SIG_IGN); /* a premature end... */
if(mode & PIPE_WRITE){
close(ichildd);
syspipe->ofilep = fdopen(oparentd, "w");
}
if(mode & PIPE_READ){
close(ochildd);
syspipe->ifilep = fdopen(iparentd, "r");
}
dprint(5, (debugfile, "PID: %d, COMMAND: %s\n",syspipe->pid,command));
}
else{
if(mode & (PIPE_WRITE | PIPE_READ)){
if(mode & PIPE_WRITE){
close(oparentd);
close(ichildd);
}
if(mode & PIPE_READ){
close(iparentd);
close(ochildd);
}
}
else{
ClearScreen();
ps_global->mangled_screen = 1;
}
if(mode & PIPE_RESET)
Raw(1);
#ifdef SIGCHLD
(void) signal(SIGCHLD, SIG_DFL);
#endif
q_status_message1(SM_ORDER,3,3, "Error executing external command: %s",
error_description(errno));
fs_give((void **)&syspipe);
if(outfile)
fs_give((void **)outfile);
dprint(1, (debugfile, "CAN'T FORK FOR COMMAND: %s\n", command));
}
return(syspipe);
}
/*----------------------------------------------------------------------
Close pipe previously allocated and wait for child's death
Args: syspipe -- address of pointer to struct returned by open_system_pipe
Returns: returns exit status of child or -1 if invalid syspipe
----*/
int
close_system_pipe(syspipe)
PIPE_S **syspipe;
{
WaitType stat;
int status;
if(!syspipe || !*syspipe)
return(-1);
if((*syspipe)->ofilep)
fclose((*syspipe)->ofilep);
if((*syspipe)->ifilep)
fclose((*syspipe)->ifilep);
#ifdef SIGCHLD
{
SigType (*alarm_sig)();
int old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
/*
* remember the current SIGALRM handler, and make sure it's
* installed when we're finished just in case the longjmp
* out of the SIGCHLD handler caused sleep() to lose it.
* Don't pay any attention to that man behind the curtain.
*/
alarm_sig = signal(SIGALRM, SIG_IGN);
(void) signal(SIGALRM, alarm_sig);
F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
ps_global->noshow_timeout = 1;
while(!child_signalled){
new_mail(0, 2, 0); /* wake up and prod server */
if(!child_signalled){
if(setjmp(child_state) == 0){
child_jump = 1; /* prepare to wake up */
sleep(600); /* give it 5mins to happend */
}
#ifdef POSIX_SIGNALS
else
sigrelse(SIGCHLD); /* unblock signal after longjmp */
#endif
}
child_jump = 0;
}
ps_global->noshow_timeout = 0;
F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
(void) signal(SIGALRM, alarm_sig);
(void) signal(SIGCHLD, SIG_DFL);
}
#endif
/*
* Call c-client's pid reaper to wait() on the demise of our child,
* then fish out its exit status...
*/
grim_pid_reap_status((*syspipe)->pid, 0, &stat);
status = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
/*
* restore original handlers...
*/
(void)signal(SIGINT, (*syspipe)->isig);
(void)signal(SIGHUP, (*syspipe)->hsig);
(void)signal(SIGQUIT, (*syspipe)->qsig);
if((*syspipe)->mode & PIPE_RESET) /* restore our tty modes */
Raw(1);
if(!((*syspipe)->mode & (PIPE_WRITE | PIPE_READ))){
ClearScreen(); /* No I/O to forked child */
ps_global->mangled_screen = 1;
}
fs_give((void **)syspipe);
return(status);
}